#!/usr/bin/ruby

require 'gosu'
require 'chipmunk'
require 'yaml'


Images = Hash.new
Controls = Hash.new

require 'Config'

require 'profile' if PROFILE

require 'Utility'
require 'Camera'
require 'Object'
require 'Menu'

require 'Player'
require 'Vehicles'
require 'Turret'
require 'Shot'
require 'World'
require 'Cursor'
require 'State'
require 'Components'
require 'Effects'
require 'Map'

class Game < Gosu::Window
  attr_reader :space, :camera
  attr_accessor :objects, :player, :cursor
  attr_accessor :remove_objects
  attr_accessor :states
	attr_accessor :map
  attr_reader :current_state
  
  def initialize    
    
    def init_keyboard_constants()
      %w{a b c d e f g h i j k l m n o p q r s t u v w x y z}.each do |letter|
        eval "Gosu::Kb#{letter.upcase} = #{self.char_to_button_id(letter)}"
      end
    end
    
    super(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_IS_FULLSCREEN)
    $game = self
    
    # create simulation space
    @space = CP::Space.new()
    @space.damping = 0.8
    
    @objects = []
    @map_objects = []
    
    # delta time between updates
    $dt = (1.0/60.0)
    
    # the basics of the game
    @player = nil
    @camera = Camera.new
    @states = {}
    
    # run all collisions through this function
    @space.set_default_collision_func {
      |shape1, shape2|
      obj1 = object_for_shape shape1
      obj2 = object_for_shape shape2
      
      if (obj1 != nil and obj2 != nil) then 
        
        if (obj1.height == obj2.height or 
          (obj1.collides_with_air or obj2.collides_with_air)) then
        
          v1 = obj1.collided_with obj2
          v2 = obj2.collided_with obj1
        end
      
        v1 == true && v2 == true
      end
      
    }
    
    @remove_objects = []
  end
  
  def enter_state(state_id)
    @current_state.leave if @current_state
    if state_id.is_a? State then
      @current_state = state_id
    else
      @current_state = @states[state_id]
    end
    @current_state.enter
  end
  
  def update  
    SUBSTEPS.times do
      @remove_objects.uniq! # don't delete objects twice (memory surprise)
      @remove_objects.each do |obj|
        # if the object is physically simulated, we need to remove it from the world
        if obj.is_a? MovableObject then
          if obj.static then
            @space.remove_static_shape obj.shape
          else
            @space.remove_body obj.shape.body
            @space.remove_shape obj.shape
          end
        end
        @objects.delete_if {|o| o == obj}
        @map_objects.delete_if {|o| o == obj}
      end
      @remove_objects.clear
      
      @space.step($dt)
    end
    
    @objects.each do |object| 
      object.update if object.active
    end
    
    @player.update
  end
  
  def draw
    
    @objects.each do |object| 
      object.draw  if object.active
      object.draw_hud  if object.active
      
    end
    
    if DRAW_DEBUG_LINES then
      @objects.each do |object|
        object.draw_debug
      end
    end
  end
  
  def button_down(id)
    # escape = leave (maybe want to change this later?)
    if id == Gosu::Button::KbEscape
      @current_state.exit_state
    end
    player.button_down(id)
  end
  
  def load_map()#file_name)
    file_name = path_for "maps/world1.yml"
    unload_map
    
    f = File.new(file_name)
    data = YAML.load(f)
    
    # load background
    bg_image = data["background"]["image"].to_sym
    @map_objects << Background.new(Images[bg_image])
    
    # todo: load a blueprint
    player_position = data["player_start"]
    #player_pawn = Crawler.new(player_position["x"], player_position["y"])
    player_pawn = Tank.new(player_position["x"], player_position["y"])
    $game.player.pawn = player_pawn
    $game.camera.follows_object player_pawn
    
    @map_objects << player_pawn
    
    engine = $component_catalogue['eng-builtin-wheels'].clone
    #engine = $component_catalogue[JetEngine][0].clone
    engine.add_to player_pawn

    weapon = $component_catalogue['wep-builtin-blaster'].clone
    weapon.add_to player_pawn, 1
    weapon = $component_catalogue['wep-builtin-5-rocket-launcher'].clone
    weapon.add_to player_pawn, 2
    weapon = $component_catalogue['wep-builtin-laserbolt'].clone
    weapon.add_to player_pawn, 1

    
    data["world_objects"].each do 
      |object_data|
      obj_class = $spawnable_objects[object_data["type"]]
      obj_x = object_data["x"]
      obj_y = object_data["y"]
      obj = obj_class.new(obj_x, obj_y)
      if object_data["params"] then
        obj.load_map_params object_data["params"]
      end
      
      @map_objects << obj
    end
  end
  
  def unload_map
    @map_objects.each do
      |o|
      o.remove
    end
    @map_objects.clear
  end
  
  def close
    Profiler__::stop_profile if PROFILE
    Profiler__::print_profile($stdout) if PROFILE
    super
  end
  
  def cursor
    # if we are asked for a cursor, create one if there isn't one
    Cursor.new unless @cursor != nil
    @cursor
  end
  
end

def init_game
  Game.new
  
  require 'Controls'
  
  load_media
  initialize_components
#  initialize_blueprints
  
  #playerPawn = Jet.new
  
  Player.new(nil)
  
  
  #Player.new(playerPawn)
  
  #Background.new(Images[:background])
  
  #turret = Turret.new(0,100)
  
  #wall = Wall.new(200,-100)
  #wall = Wall.new(150,-100)
  
  #wall = Wall.new(0,-100)
  #wall = Wall.new(-50,-100)
  
  Cursor.new
  
  #$game.camera.follows_object nil
  
  #$game.player.pawn = menu
  #$game.player.pawn = $game.camera
  
  @map = path_for "maps/world1.yml"
  
  init_states
  
  $game.enter_state :main_menu
  
	Flash.new(0xff000000, 1.0) # fade in
	
  $game.show
end

def load_media
  load_img :crawler
  load_img :shot
  load_img :background
  load_img :turret_base
  load_img :turret_gun
  load_img :rocket
  load_img :laser_blue
  
  load_img :tank_chassis
  load_img :tank_turret
  
  load_img :smoke1
  load_img :smoke2
	load_img :explosion
  load_img :flames
  
  load_img :cursor_target
  load_img :cursor_target_disabled
end

def load_img(name)
  file = path_for("/media/" + name.to_s + ".png")	
  Images[name] = Gosu::Image.new($game, file, false)
end

def path_for(name)
        $path = '.' if $path == nil
	$path + "/" + name
end

Profiler__::start_profile if PROFILE
init_game


